package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"time"
)

const (
	routineCountTotal = 5 //限制线程数
)

type Glimit struct {
	Num int
	C   chan struct{}
}

func NewG(num int) *Glimit {
	return &Glimit{
		Num: num,
		C:   make(chan struct{}, num),
	}
}

func (g *Glimit) Run(f func()) {
	g.C <- struct{}{}
	go func() {
		f()
		<-g.C
	}()
}

var client1 *http.Client

// 3.优雅地限制线程池
// 巧妙地使用go带缓冲区的通道来实现goroutine控制, 更加简洁, 并且这种方式在多个项目中都可以复用. 不必像第二种方式一样每次都实现一个worker函数

func main() {
	var numberTasks = [5]string{"13856789111", " 13812345678", "13399887771", "15866612333", "18813813813"}

	g := NewG(routineCountTotal)
	wg := &sync.WaitGroup{}
	client1 = &http.Client{}
	beg := time.Now()
	for i := 0; i < len(numberTasks); i++ {
		wg.Add(1)
		task := numberTasks[i]
		g.Run(func() {
			respBody, err := NumberQueryRequest3(task)
			if err != nil {
				fmt.Printf("error occurred in NumberQueryRequest: %s\n", task)
			} else {
				fmt.Printf("response data: %s\n", string(respBody))
			}
			wg.Done()
		})
	}
	wg.Wait()
	fmt.Printf("time consumed: %fs\n", time.Now().Sub(beg).Seconds())
}

//GET请求的通用写法, 注意复用client即可.
func NumberQueryRequest3(keyword string) (body []byte, err error) {
	start := time.Now()
	url := fmt.Sprintf("https://api.binstd.com/shouji/query?appkey=df2720f76a0991fa&shouji=%s", keyword)
	resp, err := client1.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		data, _ := ioutil.ReadAll(resp.Body)
		return nil, fmt.Errorf("response status code is not OK, response code is %d, body:%s", resp.StatusCode, string(data))
	}
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
	return body, nil
}
